package com.kevin.v2.spring.formework.webmvc.servlet;

import com.kevin.v1.springframework.annotation.KvController;
import com.kevin.v1.springframework.annotation.KvRequestMapping;
import com.kevin.v2.spring.formework.context.KvApplicationContext;
import com.kevin.v2.spring.formework.webmvc.servlet.mvc.KvHandlerAdapter;
import lombok.extern.slf4j.Slf4j;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * MVC 容器启动入口
 */
@Slf4j
public class KvDispatchServlet extends HttpServlet {

   private final String LOCALTION = "contextConfigLocation";

   private KvApplicationContext applicationContext;

   //存放handlerMapping
   private List<KvHandlerMapping> handlerMappings = new ArrayList<>();

   //存放每个hadlermaping 对应的handlerAdapter
   private Map<KvHandlerMapping, KvHandlerAdapter> handlerAdapters = new HashMap<>();


   private List<KvViewResolver> viewResolvers = new ArrayList<>();


   @Override
   protected void doGet (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
      doPost(req, resp);
   }

   @Override
   protected void doPost (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
      try {
         doDispatch(req, resp);
      } catch (Exception e) {
         e.printStackTrace();
         resp.getWriter().write("404");
         log.error(Arrays.toString(e.getStackTrace()));
      }
   }

   private void doDispatch (HttpServletRequest req, HttpServletResponse resp) throws Exception {
      KvHandlerMapping handlerMapping = getHandler(req);
      if (handlerMapping == null) {
         resp.getWriter().write("404");
      }

      KvHandlerAdapter adapter = getHandlerAdapter(handlerMapping);

      ModelAndView modelAndView = adapter.handle(req, resp, handlerMapping);

      proessDospatchResult(req, resp, modelAndView);
   }

   /**
    * 最后一步响应结果到页面
    *
    * @param req
    * @param resp
    * @param modelAndView
    */
   private void proessDospatchResult (HttpServletRequest req, HttpServletResponse resp, ModelAndView modelAndView) throws Exception {
      if (viewResolvers.isEmpty()) return;
      for (KvViewResolver viewResolver : viewResolvers) {
         KvView view = viewResolver.resolveViewName(modelAndView.getView(), null);
         if (view != null) {
            view.render(modelAndView.getModel(), req, resp);
            return;
         }
      }

   }


   private KvHandlerMapping getHandler (HttpServletRequest req) {
      String url = req.getRequestURI();
      log.debug("url" + url);
      String contextPath = req.getContextPath();
      log.debug("contextPath" + contextPath);
      url = url.replace(contextPath, "").replace("/+", "");
      for (KvHandlerMapping handlerMapping : handlerMappings) {
         Matcher matcher = handlerMapping.getRegx().matcher(url);
         if (matcher.matches()) {
            return handlerMapping;
         }
      }
      return null;
   }

   private KvHandlerAdapter getHandlerAdapter (KvHandlerMapping handlerMapping) {
      if (handlerAdapters.isEmpty()) return null;
         KvHandlerAdapter adapter = handlerAdapters.get(handlerMapping);
      if (adapter != null && adapter.supports(handlerMapping))
         return handlerAdapters.get(handlerMapping);

      return null;
   }


   @Override
   public void init (ServletConfig config) throws ServletException {
      //初始化上下文容器
      applicationContext = new KvApplicationContext(config.getInitParameter(LOCALTION));
      //初始化mvc九大组件 这里用了模板模式加策略模式
      initStrategies(applicationContext);

   }

   /**
    * Initialize the strategy objects that this servlet uses.
    * <p>May be overridden in subclasses in order to initialize further strategy objects.
    */
   //初始化策略
   protected void initStrategies (KvApplicationContext context) {
      //多文件上传的组件
      initMultipartResolver(context);
      //初始化本地语言环境
      initLocaleResolver(context);
      //初始化模板处理器
      initThemeResolver(context);
      //handlerMapping
      initHandlerMappings(context);
      //初始化参数适配器
      initHandlerAdapters(context);
      //初始化异常拦截器
      initHandlerExceptionResolvers(context);
      //初始化视图预处理器
      initRequestToViewNameTranslator(context);
      //初始化视图转换器
      initViewResolvers(context);
      //初始化页面缓存机制
      initFlashMapManager(context);
   }


   private void initHandlerMappings (KvApplicationContext context) {
      //从上下文中拿到 所有beanName
      String [] beanNames = context.getBeanDefinitionNames();
      String baseurl = null;
      for (String beanName : beanNames) {
         Object controller = context.getBean(beanName);
         Class<?> clazz = controller.getClass();
         if (!clazz.isAnnotationPresent(KvController.class)) {
            continue;
         }
         //获取该Controller的访问路由
         if (clazz.isAnnotationPresent(KvRequestMapping.class)) {
            KvRequestMapping requestMapping = clazz.getAnnotation(KvRequestMapping.class);
            baseurl = requestMapping.value().trim();
            Method[] methods = clazz.getMethods();
            for (Method method : methods) {
               if (!method.isAnnotationPresent(KvRequestMapping.class)) continue;
               KvRequestMapping methodRequestMapping = method.getAnnotation(KvRequestMapping.class);
               //拼接访问路由
               String regx = ("/" + baseurl + methodRequestMapping.value().trim()).replaceAll("\\*", "/").replaceAll("/+", "/");
               Pattern pattern = Pattern.compile(regx);
               KvHandlerMapping handlerMapping = new KvHandlerMapping(pattern, method, controller);
               //存放到一个list
               handlerMappings.add(handlerMapping);
               log.debug("regx:" + regx + "handerMapping" + handlerMapping.toString());
            }

         }
      }
   }

   private void initHandlerAdapters (KvApplicationContext context) {

      //KvHandlerMapping-->KvHandlerAdapter对应起来
      for (KvHandlerMapping handlerMapping : handlerMappings) {
         handlerAdapters.put(handlerMapping, new KvHandlerAdapter());
      }


   }

   private void initViewResolvers (KvApplicationContext context) {
//解决页面名字和模板文件关联的问题
      String templateRoot = context.getProperties().getProperty("templateRoot");
      String templateRootPath = this.getClass().getClassLoader().getResource(templateRoot).getFile();
      File templateRootDir = new File(templateRootPath);
      for (File template : templateRootDir.listFiles()) {
         this.viewResolvers.add(new KvViewResolver(templateRoot));
      }
   }


   private void initHandlerExceptionResolvers (KvApplicationContext context) {
   }

   private void initRequestToViewNameTranslator (KvApplicationContext context) {
   }

   private void initThemeResolver (KvApplicationContext context) {
   }

   private void initLocaleResolver (KvApplicationContext context) {
   }

   private void initMultipartResolver (KvApplicationContext context) {
   }

   private void initFlashMapManager (KvApplicationContext context) {
   }


}
